
#include <stdio.h>
#include "cc1101.h"


#define CC1101_SET_CSN_HIGH()           do { if (pfunc.spi_csn_level != NULL) pfunc.spi_csn_level(CC1101_SPI_CSN_LEVEL_HIGH); } while (0)
#define CC1101_SET_CSN_LOW()            do { if (pfunc.spi_csn_level != NULL) pfunc.spi_csn_level(CC1101_SPI_CSN_LEVEL_LOW); } while (0)

#define CC1101_DELAY_MS(s)              do {  if (pfunc.delay_ms != NULL) pfunc.delay_ms(s); } while (0)


//10, 7, 5, 0, -5, -10, -15, -20, dbm output power, 0x12 == -30dbm
const unsigned char PaTabel[ ] = { 0xc0, 0xC8, 0x84, 0x60, 0x68, 0x34, 0x1D, 0x0E};
static const unsigned char CC1101InitData[ 22 ][ 2 ]= 
{
  { CC1101_IOCFG0,      0x06 },
  { CC1101_FIFOTHR,     0x47 },
  { CC1101_PKTCTRL0,    0x05 },
  { CC1101_CHANNR,      0x96 }, //430M
  { CC1101_FSCTRL1,     0x06 },
  { CC1101_FREQ2,       0x0F },
  { CC1101_FREQ1,       0x62 },
  { CC1101_FREQ0,       0x76 },
  { CC1101_MDMCFG4,     0xF6 },
  { CC1101_MDMCFG3,     0x43 },
  { CC1101_MDMCFG2,     0x13 },
  { CC1101_DEVIATN,     0x15 },
  { CC1101_MCSM0,       0x18 },
  { CC1101_FOCCFG,      0x16 },
  { CC1101_WORCTRL,     0xFB },
  { CC1101_FSCAL3,      0xE9 },
  { CC1101_FSCAL2,      0x2A },
  { CC1101_FSCAL1,      0x00 },
  { CC1101_FSCAL0,      0x1F },
  { CC1101_TEST2,       0x81 },
  { CC1101_TEST1,       0x35 },
  { CC1101_MCSM1,       0x3B },
};

static cc1101_func_t pfunc = 
{
    .spi_transfer_byte  = NULL,
    .spi_csn_level      = NULL,
    .gdo0_input         = NULL,
    .gdo2_input         = NULL,
    .delay_ms           = NULL
};


void cc1101_register_spi_func(cc1101_func_t *func)
{
    pfunc.spi_transfer_byte = func->spi_transfer_byte;
    pfunc.spi_csn_level     = func->spi_csn_level;
    pfunc.gdo0_input        = func->gdo0_input;
    pfunc.gdo2_input        = func->gdo2_input;
    pfunc.delay_ms          = func->delay_ms;
}

void CC1101_Write_Cmd(unsigned char cmd)
{
    // printf("%s: cmd %02x\n", __func__, cmd);
    CC1101_SET_CSN_LOW();
    
    if (pfunc.spi_transfer_byte != NULL)
        pfunc.spi_transfer_byte(cmd);
    
    CC1101_SET_CSN_HIGH();
}

void CC1101_Write_Reg(unsigned char addr, unsigned char value)
{
    // printf("%s: addr %02x value %02x\n", __func__, addr, value);
    CC1101_SET_CSN_LOW();
    
    if (pfunc.spi_transfer_byte != NULL)
    {
        pfunc.spi_transfer_byte(addr);
        pfunc.spi_transfer_byte(value);
    }
    
    CC1101_SET_CSN_HIGH();
}

void CC1101_Write_Multi_Reg(unsigned char addr, unsigned char *pbuf, unsigned char len)
{
    unsigned char i;

    // printf("%s: addr %02x pbuf %p len %d\n", __func__, addr, pbuf, len);
    CC1101_SET_CSN_LOW();                  
    
    if (pfunc.spi_transfer_byte != NULL)
    {
        pfunc.spi_transfer_byte(addr|WRITE_BURST);
        for (i=0; i<len; i++)
            pfunc.spi_transfer_byte(pbuf[i]);
    } 
    
    CC1101_SET_CSN_HIGH();                 
}

unsigned char CC1101_Read_Reg(unsigned char addr)
{
    unsigned char retval = 0;

    // printf("%s: enter addr %02x\n", __func__, addr);
    CC1101_SET_CSN_LOW();

    if (pfunc.spi_transfer_byte != NULL)
    {
        pfunc.spi_transfer_byte(addr|READ_SINGLE);
        retval = pfunc.spi_transfer_byte(0xff);
    }
    
    CC1101_SET_CSN_HIGH();
    // printf("%s: exit value %02x\n", __func__, retval);
    return retval;
}

unsigned char CC1101_Read_Status(unsigned char addr)
{
    unsigned char retval = 0;
    
    // printf("%s: enter addr %02x\n", __func__, addr);
    CC1101_SET_CSN_LOW();

    if (pfunc.spi_transfer_byte != NULL)
    {
        pfunc.spi_transfer_byte(addr|READ_BURST);
        retval = pfunc.spi_transfer_byte(0xff);
    }
    
    CC1101_SET_CSN_HIGH();
    // printf("%s: exit value %02x\n", __func__, retval);
    return retval;
}

void CC1101_Read_Multi_Reg(unsigned char addr, unsigned char *pbuf, unsigned char rdsize)
{
    unsigned char i;
    
    // printf("%s: enter addr %02x pbuf %p rdsize %d\n", __func__, addr, pbuf, rdsize);
    CC1101_SET_CSN_LOW( );
    
    if (pfunc.spi_transfer_byte != NULL)
    {
        pfunc.spi_transfer_byte(addr|READ_BURST);
        for (i=0; i<rdsize; i++)
            pbuf[i] = pfunc.spi_transfer_byte(0xff);
    }
    
    CC1101_SET_CSN_HIGH( );
}

int CC1101_Set_Mode(CC1101_ModeType mode)
{
    int cnt;

    printf("%s: enter\n", __func__);
    
    if(mode == TX_MODE)
    {
        // CC1101_Write_Reg( CC1101_IOCFG0,0x46 );
        CC1101_Write_Reg(CC1101_IOCFG0, 0x06);
        CC1101_Write_Cmd( CC1101_STX );
        printf("%s: set TX mode\n", __func__);

        printf("%s: gdo0_input %d, start gdo0 should be 0\n", __func__, pfunc.gdo0_input());
        if (0 != pfunc.gdo0_input())
        {
            printf("%s: start gdo0 level invalid\n", __func__);
            return -1;
        }

        printf("%s: gdo0_input %d, wait gdo0 to 1\n", __func__, pfunc.gdo0_input());
        cnt = 0;
        while (0 == pfunc.gdo0_input())
        {
            /*
             *  @brief: generally gdo0 change to level 1 need 106ms
             */
            if (cnt++ > 150)
            {
                printf("%s: wait gdo0 to level 1 timeout %dms\n", __func__, 150);
                return -2;
            }
            CC1101_DELAY_MS(1);
        }
        printf("%s: in TX loop a cnt %d\n", __func__, cnt);
    }
    else if(mode == RX_MODE)
    {
        // CC1101_Write_Reg(CC1101_IOCFG0,0x46);
        CC1101_Write_Reg(CC1101_IOCFG0, 0x01);
        CC1101_Write_Cmd( CC1101_SRX );
        printf("%s: set RX mode\n", __func__);
    }

    printf("%s: exit\n", __func__);
    return 0;
}

void CC1101_Set_Idle_Mode( void )
{
    CC1101_Write_Cmd( CC1101_SIDLE );
}

void C1101_WOR_Init( void )
{
    CC1101_Write_Reg(CC1101_MCSM0,0x18);        
    CC1101_Write_Reg(CC1101_WORCTRL,0x78); 
    CC1101_Write_Reg(CC1101_MCSM2,0x00);
    CC1101_Write_Reg(CC1101_WOREVT1,0x8C);
    CC1101_Write_Reg(CC1101_WOREVT0,0xA0);
    CC1101_Write_Cmd( CC1101_SWORRST );
}

void CC1101_Set_Address( unsigned char Address, CC1101_AddrModeType AddressMode)
{
    unsigned char btmp = 0;
    
    CC1101_Write_Reg( CC1101_ADDR, Address );

    btmp = CC1101_Read_Reg( CC1101_PKTCTRL1 ) & ~0x03;
    if( AddressMode == BROAD_ALL )
    {

    }
    else if( AddressMode == BROAD_NO  )
    { 
        btmp |= 0x01;
    }
    else if( AddressMode == BROAD_0   )
    { 
        btmp |= 0x02;
    }
    else if( AddressMode == BROAD_0AND255 ) 
    {
        btmp |= 0x03;
    } 

    CC1101_Write_Reg( CC1101_PKTCTRL1, btmp);
}

void CC1101_Set_Sync( unsigned short Sync )
{
    CC1101_Write_Reg( CC1101_SYNC1, 0xFF & ( Sync >> 8 ) );
    CC1101_Write_Reg( CC1101_SYNC0, 0xFF & Sync );
}

void CC1101_Clear_TxBuffer( void )
{
    CC1101_Set_Idle_Mode( );
    CC1101_Write_Cmd( CC1101_SFTX );
}

void CC1101_Clear_RxBuffer( void )
{
    CC1101_Set_Idle_Mode();
    CC1101_Write_Cmd( CC1101_SFRX );
}

int CC1101_Tx_Packet(unsigned char *pbuf, unsigned char len, CC1101_TxDataModeType mode)
{
    unsigned char Address, ret;
    int cnt;

    if (pfunc.gdo0_input == NULL || pfunc.delay_ms == NULL)
        return -1;

    // printf("%s: pbuf %p len %d mode %d\n", __func__, pbuf, len, mode);
    
    if (mode == BROADCAST)             
        Address = 0; 
    else if (mode == ADDRESS_CHECK)    
        Address = CC1101_Read_Reg(CC1101_ADDR); 

    CC1101_Clear_TxBuffer();
    
    ret = CC1101_Read_Reg(CC1101_PKTCTRL1);
    printf("%s: CC1101_Read_Reg(CC1101_PKTCTRL1) ret 0x%02x\n", __func__, ret);
    if ((ret & 0x03) != 0) 
    {
        printf("%s: write len %d, addr 0x%02x\n", __func__, len+1, Address);
        CC1101_Write_Reg(CC1101_TXFIFO, len + 1);
        CC1101_Write_Reg(CC1101_TXFIFO, Address);
    }
    else
    {
        CC1101_Write_Reg( CC1101_TXFIFO, len );
    }

    CC1101_Write_Multi_Reg( CC1101_TXFIFO, pbuf, len );
    CC1101_Set_Mode( TX_MODE );

    printf("%s: gdo0_input %d, wait gdo0 to 0\n", __func__, pfunc.gdo0_input());
    cnt = 0;
    while(0 != pfunc.gdo0_input())
    {
        /*
         *  @brief: generally gdo0 change to level 0 need 72ms
         */
        if (cnt++ > 100)
        {
            printf("%s: wait gdo0 to level 0 timeout %dms\n", __func__, 100);
            return -2;
        }
        CC1101_DELAY_MS(1);
    } 
    printf("%s: in TX loop b cnt %d\n", __func__, cnt);
    return len;
}

unsigned char CC1101_Get_RxCounter( void )
{
    return ( CC1101_Read_Status( CC1101_RXBYTES ) & BYTES_IN_RXFIFO );  
}

char CC1101_Rx_Packet( unsigned char *RxBuff, int maxsize, int timeout)
{
    unsigned char l_PktLen = 0, ret;
    unsigned char l_Status[ 2 ] = { 0 };
    int cnt;

    if (pfunc.gdo0_input == NULL || pfunc.delay_ms == NULL)
        return 0;

    printf("%s: enter\n", __func__);

    cnt = 0;
    while(0 == pfunc.gdo0_input())
    {
        if (cnt++ > timeout)
            return 0;

        CC1101_DELAY_MS(1);
    }

    if ((ret = CC1101_Get_RxCounter()) == 0)
        return 0;
    
    printf("%s: CC1101_Get_RxCounter ret %d\n", __func__, ret);
    l_PktLen = CC1101_Read_Reg( CC1101_RXFIFO );
    printf("%s: pkg addr(1) + data len %d\n", __func__, l_PktLen);
    
    ret = CC1101_Read_Reg(CC1101_PKTCTRL1);
    printf("%s: CC1101_Read_Reg(CC1101_PKTCTRL1) ret %02x\n", __func__, ret);
    if((ret & 0x03) != 0)
    {
       ret = CC1101_Read_Reg(CC1101_RXFIFO);
       printf("%s: pkg addr %02x\n", __func__, ret);
    }
    if( l_PktLen == 0 )
        return 0;
    
    l_PktLen--;
    if (l_PktLen > maxsize)
    {
        printf("%s: recv len %d larger than buff size %d\n", __func__, l_PktLen, maxsize);
        return 0;
    }

    CC1101_Read_Multi_Reg(CC1101_RXFIFO, RxBuff, l_PktLen);
    CC1101_Read_Multi_Reg(CC1101_RXFIFO, l_Status, 2);
    printf("%s: l_Status[0] %02x l_Status[1] %02x\n", __func__, l_Status[0], l_Status[1]);

    CC1101_Clear_RxBuffer();

    if((l_Status[1] & CRC_OK) == 0)
        return 0; 

    return l_PktLen; 
}

static void CC1101_Reset( void )
{
    CC1101_SET_CSN_HIGH( );
    CC1101_SET_CSN_LOW( );
    CC1101_SET_CSN_HIGH( );
    CC1101_DELAY_MS( 1 );
    CC1101_Write_Cmd( CC1101_SRES );
}

void CC1101_Init(void)
{
    unsigned char i = 0;

    printf("%s: enter\n", __func__);

    CC1101_Reset();

    // get cc1101 version
    printf("%s: version %d\n", __func__, CC1101_Read_Reg(CC1101_VERSION));

    for( i = 0; i < 22; i++ )
        CC1101_Write_Reg( CC1101InitData[i][0], CC1101InitData[i][1] );

    CC1101_Set_Address( 0x05, BROAD_0AND255 );
    CC1101_Set_Sync( 0x8799 );
    CC1101_Write_Reg(CC1101_MDMCFG1, 0x72);

    CC1101_Write_Multi_Reg( CC1101_PATABLE, (unsigned char*)PaTabel, 8 );
    printf("%s: exit\n", __func__);
}

void cc1101_dump_register(void)
{
    int i;
    unsigned char value;

#if 0
    // printf("%s: enter\n", __func__);
    for (i=0; i<22; i++)
    {
        value = CC1101_Read_Reg(CC1101InitData[i][0]);
        // printf("%s: read reg %02x value %02x\n", __func__, CC1101InitData[i][0], value);
    }
    // printf("%s: exit\n", __func__);
#else
    printf("%s: list\n", __func__);
    for (i=0; i<0x40; i++)
    {
        value = CC1101_Read_Reg(i);
        printf("read reg %02x value %02x\n", i, value);
    }
#endif
}

unsigned int cc1101_get_rssi(void)
{
    return CC1101_Read_Reg(CC1101_RSSI);
}
